package com.sanmao5.wecom.util.http;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * HttpClient 一定要是单例的，因为 HttpClient 的建立是很耗时间的一件事情
 * 平时在工作中可能会遇到，当高 qps 的时候第一次请求可能会特别慢，但以后的
 * 请求就会特别快
 * <p>
 * 面试题：HTTP_CLIENT 怎么优化？
 * 1. 先注册 Registry
 * 2. 注册连接池
 * 3. 针对 HttpConfig 做优化
 * 4. HTTP_CLIENT 是单例的，所以可以做一些各种各样的东西
 *
 * @author 黄侯淳
 */
@Slf4j
public class HttpUtil {

    /**
     * 配置连接池、ssl、https 的一些东西
     * 包括 http 连接池的数量、路由、超时时间等东西
     */
    private static final CloseableHttpClient HTTP_CLIENT;

    static {
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", SSLConnectionSocketFactory.getSocketFactory())
                .build();
        // 连接池：管理 http 连接
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(500);
        connectionManager.setDefaultMaxPerRoute(500);
        connectionManager.setDefaultSocketConfig(
                SocketConfig.custom()
                        .setSoTimeout(15, TimeUnit.SECONDS)
                        .setTcpNoDelay(true).build()
        );
        connectionManager.setValidateAfterInactivity(TimeValue.ofSeconds(15));

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(Timeout.ofSeconds(60))
                .setConnectionRequestTimeout(Timeout.ofSeconds(60))
                .setExpectContinueEnabled(true)
                .setResponseTimeout(Timeout.ofSeconds(60))
                .build();

        HTTP_CLIENT = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .disableAutomaticRetries() // 禁止自动重连
                .build();
    }

    public static String get(String url, List<Pair<String, String>> pairList,
                             Map<String, String> headerMap) throws Exception {
        if (CollUtil.isNotEmpty(pairList)) {
            url = url + "?" + buildParam(pairList);
        }
        if (log.isInfoEnabled()) {
            log.info("GET URL: {}", url);
            log.info("HeaderMap: {}", JSONUtil.toJsonStr(headerMap));
        }
        HttpGet httpGet = new HttpGet(url);
        if (Objects.nonNull(headerMap) && !headerMap.isEmpty()) {
            headerMap.forEach(httpGet::addHeader);
        }
        CloseableHttpResponse response = null;
        try {
            response = HTTP_CLIENT.execute(httpGet, HttpClientContext.create());
            return EntityUtils.toString(response.getEntity());
        } finally {
            if (null != response) {
                EntityUtils.consume(httpGet.getEntity());
            }
        }
    }

    public static String post(String url, List<Pair<String, String>> pairList,
                              Map<String, String> headerMap) throws Exception {
        if (CollUtil.isNotEmpty(pairList)) {
            url = url + "?" + buildParam(pairList);
        }
        // 声明一个 httpPost 对象
        HttpPost httpPost = new HttpPost(url);
        if (Objects.nonNull(headerMap) && !headerMap.isEmpty()) {
            headerMap.forEach(httpPost::addHeader);
        }
        CloseableHttpResponse response = null;
        try {
            response = HTTP_CLIENT.execute(httpPost);
            return EntityUtils.toString(response.getEntity());
        } finally {
            if (null != response) {
                EntityUtils.consume(response.getEntity());
            }
        }
    }

    private static String buildParam(List<Pair<String, String>> pairList) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Pair<String, String> pair : pairList) {
            stringBuilder.append(pair.getKey())
                    .append("=")
                    .append(pair.getValue())
                    .append("&");
        }
        stringBuilder.setLength(stringBuilder.length() - 1);
        return stringBuilder.toString();
    }
}

